// Copyright (C) 2012 Yury Bychenkov (http://bychenkov.com).
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//  - Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//  - Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//  - Neither the name of Yury Bychenkov nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

inline void TupleBuilder::addInt64(int64 value, bool asc) {
	if (dataSize + sizeof(int64) + 1 > dataCapacity) {
		ensureCapacity(sizeof(int64) + 1);
	}

	if (value == (int64) 0x8000000000000000LL) {
		data[dataSize++] = asc ? 0x00 : 0xFF;
		return;
	}

	uint8 b;

	if (asc) {
		b = 0x49;
	} else {
		b = 0xC9;
		value = -value;
	}

	if (value == 0 || value == -1) {
		data[dataSize++] = b & 0xF0;
		return;
	}

	uint64 v = NumberUtil::bswap64((uint64) value);

	if (value > 0) {
		if (((uint32) v) == 0) {
			v >>= 32;
			b -= 4;
		}
		if (((uint16) v) == 0) {
			v >>= 16;
			b -= 2;
		}
		if (((uint8) v) == 0) {
			v >>= 8;
			b--;
		}

		data[dataSize] = b;	
		*((uint64*) (data + dataSize + 1)) = v;
		dataSize += (b & 0xF);
	} else { // value < 0
		if (((uint32) v) == 0xFFFFFFFFU) {
			v >>= 32;
			b -= 4;
		}
		if (((uint16) v) == 0xFFFFU) {
			v >>= 16;
			b -= 2;
		}
		if (((uint8) v) == 0xFFU) {
			v >>= 8;
			b--;
		}
		
		data[dataSize] = b ^ 0x4F;	
		*((uint64*) (data + dataSize + 1)) = v;
		dataSize += (b & 0xF);
	}
}

inline void TupleBuilder::addUInt31(uint32 value, bool asc) {
	if (dataSize + sizeof(uint32) > dataCapacity) {
		ensureCapacity(sizeof(uint32));
	}
	
	*((int32*) (data + dataSize)) = NumberUtil::bswap32(asc ? value : ~value);
	dataSize += sizeof(int32);
}

inline const uint8* TupleBuilder::build() {
	if (dataSize < 0xFF) {
		data[3] = (uint8) dataSize;
		return data + 3;
	} else {
		*((uint32*) data) = (uint32)(dataSize << 8) | 0xFF;
		return data; 
	}
}

inline void TupleBuilder::reset() {
	dataSize = 4;
}


inline Tuple::Tuple(const uint8* data_):
	dataSize(*data_ < 0xFF? *data_ : (*((uint32*) data_) >> 8)),
	data(data_ + (dataSize < 0xFF ? 1 : 4)),
	nextOffs(0)
{
}


inline int Tuple::nextBytes(uint8* buffer, uint capacity) {
	uint size = 0;
	const uint8* bytes = data + nextOffs;

	if (bytes[0] == 0x00 || bytes[0] == 0xFF) {
		nextOffs++;
		return 0;
	}
	
	if (*bytes++ <= 0x7F) {
		while (true) {
			uint8 b = *bytes++;
			if (b == 0) {
				break;
			} else if (b == 0xFE) {
				b = *bytes++;
			} else {
				b--;
			}
			if (size < capacity) {
				*buffer++ = b;
			}
			size++;
		}
	} else {
		while (true) {
			uint8 b = *bytes++;
			if (b == 0xFF) {
				break;
			} else if (b == 0x01) {
				b = *bytes++;
			} else {
				b++;
			}
			if (size < capacity) {
				*buffer++ = ~b;
			}
			size++;
		}
	}

	if (size <= capacity) {
		nextOffs = uint(bytes - data);
		return int(size) + 1;
	}

	return -int(size) - 1;
}

inline int64 Tuple::nextInt64() {
	uint8 b = data[nextOffs++];
	if (b == 0x00 || b == 0xFF) {
		return 0x8000000000000000LL;
	}

	int64 result;
	if (b & 0x40) {
		result = 0;
	} else {
		result = -1;
		b ^= 0x4F;
	}

	for (uint8 i = b & 0x0F; i > 1; i--) {
		result = (result << 8) | data[nextOffs++];
	}

	return (b & 0x80) ? -result : result;
}

inline uint32 Tuple::nextUInt31() {
	uint32 result = NumberUtil::bswap32(*((uint32*) (data + nextOffs)));
	nextOffs += sizeof(int32);
	return (result & 0x8000000) == 0 ? result : ~result;
}

inline void Tuple::reset() {
	nextOffs = 0;
}
